package com.example.demo.net;

import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 局域网
 * <p>
 * 代码中{@code NET_NODE_MAP}字段用于封装{@link NetNode}网络节点，其
 * 中{@code invoke({@link NetCommand})}方法的作用是用反射的方式唤醒
 * {@link NetNode}网络节点的方法，最终将对应节点的方法返回。
 *
 * @author ZheMing Liu
 * @since 2021.11.5
 */
@Slf4j
@Data
@Component
@Order(-1)
public class Internet implements ApplicationContextAware {

    /**
     * 包扫描路径
     */
    @Value("${internet.basepackage}")
    private String basePackage;

    /**
     * 网络节点集
     */
    @Getter
    private static final Map<String, NetNode> NET_NODE_MAP = new ConcurrentHashMap<>();

    private volatile int modify = 0;

    /**
     * 初始化网络并注册网络节点，声明了所有网络节点的公用网络{@code NetNode.Internet = this}，
     * 其中注册到{@code NET_NODE_MAP}中的网络节点的名称案例：String：weizhong NetNode：WeiZhongNetNode
     */
    public Internet() {
        NetNode.setInternet(this);
        try {
            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
            InputStream application = contextClassLoader.getResourceAsStream("application.properties");
            Properties properties = new Properties();
            properties.load(application);
            String basePackage = properties.getProperty("internet.basepackage");
            URL resource = contextClassLoader.getResource(basePackage);
            File file = new File(resource.getFile());
            File[] files = file.listFiles((a, b) -> b.endsWith(".class"));
            for (File fileNode : files) {
                Class<?> aClass = Thread.currentThread().getContextClassLoader().loadClass(basePackage.replaceAll("/", ".") + "." + fileNode.getName().replaceAll(".class", ""));
                if (aClass.isAnnotationPresent(NetNodeSign.class) && NetNode.class.isAssignableFrom(aClass)) {
                    NET_NODE_MAP.put(aClass.getSimpleName().replaceAll("NetNode", "").toLowerCase(), (NetNode) aClass.newInstance());
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 唤醒网络节点方法
     *
     * @param netCommand 请参考{@link NetCommand}
     * @return 唤醒方法的返回值
     */
    public NetResponse invoke(NetCommand netCommand) {
        NetResponse invoke = null;
        try {
            invoke = null;
            String target = netCommand.getTarget();
            assert target == null ? true : false;
            String method = netCommand.getTargetMethod();
            assert method == null ? true : false;
            Object[] args = netCommand.getTargetMethodArgs();
            NetNode netNode = NET_NODE_MAP.get(target);
            assert netNode == null ? true : false;
            invoke = netNode.execute(args);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return invoke;
    }

    /**
     * 将相关的{@link NetNode}连接到局域网
     *
     * @param name    网络节点名称，固定为类名
     * @param netNode 网络节点
     */
    public void NodeConnectToInternet(String name, NetNode netNode) {
        if (!NET_NODE_MAP.containsKey(name)) {
            NET_NODE_MAP.put(name, netNode);
        }
    }

    /**
     * 将相关的{@link NetNode}从局域网移除
     *
     * @param name 网络节点名称
     */
    public void NoderRemoveToInternet(String name) {
        if (NET_NODE_MAP.containsKey(name)) {
            NET_NODE_MAP.remove(name);
        }
    }

    /**
     * 给{@link NetNode}设置spring容器
     *
     * @param applicationContext spring容器
     * @throws BeansException spring bean相关异常
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        NetNode.setApplicationContext(applicationContext);
    }
}
